#include "cpu/cpu.h"
#include "os_cfg.h"
#include "comm/cpu_instr.h"
#include "ipc/mutex.h"
#include "core/syscall.h"
#include "cpu/irq.h"

static mutex_t mutex;
//全局段描述符表
static global_segment_descriptor  gdt_table[GDT_TABLE_SIZE];

//设置全局段描述符表
void set_global_segment(int selector,uint32_t base,uint32_t limit,uint16_t attr)
{
    global_segment_descriptor* index= gdt_table + selector;
    if(limit > 0xFFFFF)
    {
        attr |= 0x8000;   //粒度设为1
        limit /= 0x1000;  
    }
    index->limit15_0 = limit & 0xffff;
	index->base15_0 = base & 0xffff;
	index->base23_16 = (base >> 16) & 0xff;
    index->attr = attr | (((limit >> 16) & 0xf) << 8);
	index->base31_24 = (base >> 24) & 0xff;
}


//初始化GDT
void init_gdt(void) 
{
	// 全部清空
    for (int i = 0; i < GDT_TABLE_SIZE; i++) 
    {
        set_global_segment(i, 0, 0, 0);
    }
    //设置为平坦模式
    //设置代码段  特权级0  代码段 存在 可读写 32位
    set_global_segment(KERNEL_CS,0,0xFFFFFFFF,SEG_P | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_CODE | SEG_TYPE_RW | SEG_D | SEG_G);
    //设置数据段  特权级0  数据段 存在 可读写 32位
    set_global_segment(KERNEL_DS,0,0xFFFFFFFF,SEG_P | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_DATA | SEG_TYPE_RW | SEG_D | SEG_G);
    //建立系统调用门
    set_gate_descriptor((gate_descriptor*)(gdt_table+SELECTOR_SYSCALL), KERNEL_CS << 3 ,(uint32_t)(exception_handler_syscall),GATE_P_PRESENT | GATE_DPL3 | GATE_TYPE_SYSCALL | SYSCALL_PARAM_COUNT);

    
    //设置寄存器
    lgdt((uint32_t)gdt_table,sizeof(gdt_table));
}

//CPU初始化
void cpu_init (void) 
{
    mutex_init(&mutex);
    init_gdt();
}


int search_gdt_empty()
{
    mutex_lock(&mutex);
    //跳过第零项
    for(int i=1;i<GDT_TABLE_SIZE;i++)
    {
        global_segment_descriptor* index= gdt_table + i;
        if(index->attr==0)
        {
            mutex_unlock(&mutex);
            return i * sizeof(global_segment_descriptor);
        }
    }
    mutex_unlock(&mutex);
    return -1;
}



void gdt_free_sel(int sel)
{
    mutex_lock(&mutex);
    gdt_table[sel/sizeof(global_segment_descriptor)].attr=0;
    mutex_unlock(&mutex);
}

void switch_to_tss(int tss_sel)
{
    far_jump(tss_sel,0);
}